package com.ly.blog_user.security;


import com.alibaba.fastjson.JSON;
import com.ly.blog_common.constant.Constant;
import com.ly.blog_common.entity.SysUserTokenEntity;
import com.ly.blog_common.security.JwtAuthenticationToken;
import com.ly.blog_common.security.JwtUserDetails;
import com.ly.blog_common.utils.RequestUtils;
import com.ly.blog_user.service.SysUserTokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Security相关操作
 * @author Louis
 * @date Jan 14, 2019
 */
@Slf4j
public class SecurityUtils extends com.ly.blog_common.security.SecurityUtils {


    /**
     * 系统登录认证
     * @param request
     * @param username
     * @param password
     * @param authenticationManager
     * @param redisTemplate
     * @param sysUserTokenService
     * @return
     */
    public static JwtAuthenticationToken login(HttpServletRequest request,
                                               String username,
                                               String password,
                                               AuthenticationManager authenticationManager,
                                               RedisTemplate redisTemplate,
                                               SysUserTokenService sysUserTokenService) {
        JwtAuthenticationToken token = new JwtAuthenticationToken(username, password);
        token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        // 执行登录认证过程 - 返回登录成功后封装认证对象，包含权限，用户信息，盐值
        //对象解析 principal:UserDetail  credential:null    authorities:权限集合    details：request
        Authentication authentication = authenticationManager.authenticate(token);


        // 认证成功 存储认证信息
        // 生成令牌并返回给客户端
        String tokenValue = saveAuthenticationInfo(authentication, redisTemplate, sysUserTokenService, request);
        token.setToken(tokenValue);

        //需要将token放在Authentication对象，用于多服务质检token变化后，可识别
        //每个服务存在自己的上下文，第一次登陆后，修改当前用户权限，在访问时，还是当前权限。
        // JwtAuthenticationToken 封装对象，存在token - 不需要后续处理

        // 设置登录认证信息到上下文
        SecurityUtils.setAuthentication(authentication);

        return token;
    }

    /**
     * 记录登录token 和用户ID的关系
     * @param authentication
     * @param redisTemplate
     * @param sysUserTokenService
     * @param request
     * @return
     */
    private static String saveAuthenticationInfo(Authentication authentication, RedisTemplate redisTemplate, SysUserTokenService sysUserTokenService, HttpServletRequest request) {

        //生成token，并保存到数据库 -- 同时放到redis中
        //修订存放到redis中
        String token = null;
        //当前时间
        Date now = new Date();
        //过期时间
        Date expireTime = new Date(now.getTime() + Constant.EXPIRE * 1000);

        //生成一个token
        token = TokenGeneratorUtils.generateValue();

        //判断是否生成过token
        JwtUserDetails jwtUserDetails = (JwtUserDetails) authentication.getPrincipal();
        long userId = jwtUserDetails.getUserId();

        String tokenCookie = RequestUtils.getToken(request);

        SysUserTokenEntity tokenEntity = (SysUserTokenEntity)redisTemplate.opsForValue().get(tokenCookie);

        if(tokenEntity == null){
            tokenEntity = new SysUserTokenEntity();
            tokenEntity.setUserId(userId);
            tokenEntity.setUserDetail(JSON.toJSONString(jwtUserDetails));
            tokenEntity.setToken(token);
            tokenEntity.setUpdateTime(now);
            tokenEntity.setExpireTime(expireTime);

        }else{ //用户二次登录 ， 前面登录无效
            tokenEntity.setToken(token);
            tokenEntity.setUserDetail(JSON.toJSONString(jwtUserDetails));
            tokenEntity.setUpdateTime(now);
            tokenEntity.setExpireTime(expireTime);
        }

        sysUserTokenService.saveOrUpdate(tokenEntity);
        //redis 保存token - 采用token记录，反推数据
        redisTemplate.opsForValue().set(String.valueOf(token),tokenEntity,Constant.EXPIRE,TimeUnit.SECONDS);
        log.info("【用户登录记录】 - {}",JSON.toJSONString(jwtUserDetails.getUser()));

        return token;
    }

}
